﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Wydatki.WRK.Lotto.DataSets;
using ProgNetComponentsDx.Data;
using ProgNetComponents.Data.SqlCe;
using System.Data;
using ProgNetComponentsDx.Forms;
using System.Windows.Forms;
using ProgNetComponentsDx.Controls;

namespace Wydatki.Tools.Lotto
{
    public static class LottoLosowaniaTools
    {
        public static ulong Kombinacja(int n, int k)
        {
            ulong res = 1;
            int max = n-k;
            for (int i=0; i < k; i++)
                res = res * (ulong)(n - i);
            for (int i=2; i <= k; i++)
                res = res / (ulong)i;
            return res;
        }

        public const int LICZBA_KOMBINACJI_6_Z_49 = 13983816;
        public const int LICZBA_KOMBINACJI_2_Z_49 = 1176;
        public const int LICZBA_KOMBINACJI_3_Z_49 = 18424;

        public const int LICZBA_KOMBINACJI_2_Z_6 = 15;
        public const int LICZBA_KOMBINACJI_3_Z_6 = 20;

        private static object lockLosowania = new object();
        private static object lockNajczestszeLiczby = new object();
        private static object lockNajczestszeDwojki = new object();
        private static object lockNajczestszeTrojki = new object();
        private static object lockManager = new object();

        private static int _IloscLosowan = 0;
        private static int _IloscNajczestszeLiczby = 0;
        private static int _IloscNajczestszeDwojki = 0;
        private static int _IloscNajczestszeTrojki = 0;

        private static bool _LosowaniaPrecashed;
        private static bool _NajczestszeLiczbyPrecashed;
        private static bool _NajczestszeDwojkiPrecashed;
        private static bool _NajczestszeTrojkiPrecashed;

        public static bool LosowaniaPrecashed
        {
            get { return _LosowaniaPrecashed; }
        }
        public static bool NajczestszeLiczbyPrecashed
        {
            get { return _NajczestszeLiczbyPrecashed; }
        }
        public static bool NajczestszeDwojkiPrecashed
        {
            get { return _NajczestszeDwojkiPrecashed; }
        }
        public static bool NajczestszeTrojkiPrecashed
        {
            get { return _NajczestszeTrojkiPrecashed; }
        }

        private static LottoLosowaniaDataSet.WRK_LOTTO_LOSOWANIADataTable _LosowaniaTable = new LottoLosowaniaDataSet.WRK_LOTTO_LOSOWANIADataTable();

        public static LottoLosowaniaDataSet.WRK_LOTTO_LOSOWANIADataTable LosowaniaTable
        {
            get { return _LosowaniaTable; }
        }

        private static List<List<int>> _LosowaniaList = new List<List<int>>();
        private static List<NajczestszeLiczby> _NajczestszeLiczbyList = new List<NajczestszeLiczby>();
        private static List<NajczestszeLiczby> _NajczestszeDwojkiList = new List<NajczestszeLiczby>();
        private static List<NajczestszeLiczby> _NajczestszeTrojkiList = new List<NajczestszeLiczby>();

        private static SqlCeDataManager manager=null;
        private static SqlCeDataManager Manager
        {
            get
            {
                if (manager == null)
                {
                    manager = new SqlCeDataManager();
                }
                return manager;
            }
        }

        public static List<List<int>> LosowaniaList
        {
            get
            {
                lock (lockLosowania)
                {
                    if (_LosowaniaList.Count == 0)
                    {
                        if (_LosowaniaTable.Rows.Count == 0)
                            GetLosowania();
                        FillLosowaniaList();
                    }
                }
                return _LosowaniaList;
            }
        }
        public static List<NajczestszeLiczby> NajczestszeLiczby
        {
            get
            {
                bool get = false;
                lock (lockNajczestszeLiczby)
                {
                    if (_NajczestszeLiczbyList.Count == 0)
                    {
                        get = true;
                    }
                }
                if (get)
                    GetNajczestszeLiczby(1, false);
                return _NajczestszeLiczbyList;
            }
        }
        public static List<NajczestszeLiczby> NajczestszeDwojki
        {
            get
            {
                bool get = false;
                lock (lockNajczestszeDwojki)
                {
                    if (_NajczestszeDwojkiList.Count == 0)
                    {
                        get = true;
                    }
                }
                if (get)
                    GetNajczestszeLiczby(2, false);
                return _NajczestszeDwojkiList;
            }
        }
        public static List<NajczestszeLiczby> NajczestszeTrojki
        {
            get
            {
                bool get = false;
                lock (lockNajczestszeTrojki)
                {
                    if (_NajczestszeTrojkiList.Count == 0)
                    {
                        get = true;
                    }
                }
                if (get)
                    GetNajczestszeLiczby(3, false);
                return _NajczestszeTrojkiList;
            }
        }

        public static List<NajczestszeLiczby> NajczestszeLiczbyClone
        {
            get
            {
                List<NajczestszeLiczby> l = new List<NajczestszeLiczby>();
                foreach (NajczestszeLiczby item in NajczestszeLiczby)
                    l.Add(item.Clone());
                return l;
            }
        }
        public static List<NajczestszeLiczby> NajczestszeDwojkiClone
        {
            get
            {
                List<NajczestszeLiczby> l = new List<NajczestszeLiczby>();
                foreach (NajczestszeLiczby item in NajczestszeDwojki)
                    l.Add(item.Clone());
                return l;
            }
        }
        public static List<NajczestszeLiczby> NajczestszeTrojkiClone
        {
            get
            {
                List<NajczestszeLiczby> l = new List<NajczestszeLiczby>();
                foreach (NajczestszeLiczby item in NajczestszeTrojki)
                    l.Add(item.Clone());
                return l;
            }
        }

        public static int IloscLosowan
        {
            get
            {
                if (_IloscLosowan == 0)
                {
                    lock (lockLosowania)
                    {
                        GetLosowania();
                    }
                }
                return _IloscLosowan;
            }
        }
        public static int IloscNajczestszychLiczb
        {
            get
            {
                bool get = false;
                lock (lockNajczestszeLiczby)
                {
                    if (_IloscNajczestszeLiczby == 0)
                    {
                        get = true;
                    }
                }
                if (get)
                    GetNajczestszeLiczby(1, false);
                return _IloscNajczestszeLiczby;
            }
        }
        public static int IloscNajczestszychDwojek
        {
            get
            {
                bool get = false;
                lock (lockNajczestszeDwojki)
                {
                    if (_IloscNajczestszeDwojki == 0)
                    {
                        get = true;
                    }
                }
                if (get)
                    GetNajczestszeLiczby(2,  false);
                return _IloscNajczestszeDwojki;
            }
        }
        public static int IloscNajczestszychTrojek
        {
            get
            {
                bool get = false;
                lock (lockNajczestszeTrojki)
                {
                    if (_IloscNajczestszeTrojki == 0)
                    {
                        get = true;
                    }
                }
                if (get)
                    GetNajczestszeLiczby(3,  false);
                return _IloscNajczestszeTrojki;
            }
        }

        public static void ClearLosowania()
        {
            _LosowaniaTable.Clear();
            _LosowaniaList.Clear();
            _IloscLosowan = 0;
            _LosowaniaPrecashed = false;
        }
        public static void ClearNajczestszeLiczby()
        {
            _NajczestszeLiczbyList.Clear();
            _IloscNajczestszeLiczby = 0;
            _NajczestszeLiczbyPrecashed = false;
        }
        public static void ClearNajczestszeDwojki()
        {
            _NajczestszeDwojkiList.Clear();
            _IloscNajczestszeDwojki = 0;
            _NajczestszeDwojkiPrecashed = false;
        }
        public static void ClearNajczestszeTrojki()
        {
            _NajczestszeTrojkiList.Clear();
            _IloscNajczestszeTrojki = 0;
            _NajczestszeTrojkiPrecashed = false;
        }

        public static void SetLosowania(LottoLosowaniaDataSet ds)
        {
            ClearLosowania();
            _LosowaniaTable.Merge(ds.WRK_LOTTO_LOSOWANIA);
            _LosowaniaTable.AcceptChanges();
            _IloscLosowan = _LosowaniaTable.Rows.Count;
            _LosowaniaPrecashed = true;
        }

        private static void FillLosowaniaList()
        {
            _LosowaniaList.Clear();
            DataRow[] rows = _LosowaniaTable.Select(null, "WRK_LL_DATA");
            foreach (DataRow r in rows)
            {
                _LosowaniaList.Add(new List<int>(){
                    r["WRK_LL_LICZBA1"].AsInt(),
                    r["WRK_LL_LICZBA2"].AsInt(),
                    r["WRK_LL_LICZBA3"].AsInt(),
                    r["WRK_LL_LICZBA4"].AsInt(),
                    r["WRK_LL_LICZBA5"].AsInt(),
                    r["WRK_LL_LICZBA6"].AsInt()});
            }
        }

        private static void GetLosowania()
        {
            ClearLosowania();
            lock (lockManager)
            {
                Manager.FillTable(_LosowaniaTable, "SELECT * FROM WRK_LOTTO_LOSOWANIA");
            }
            _IloscLosowan = _LosowaniaTable.Rows.Count;
            _LosowaniaPrecashed = true;
        }

        static FormBackgroundWorkMultiThread multiThreadForm1;
        static FormBackgroundWorkMultiThread multiThreadForm2;
        static FormBackgroundWorkMultiThread multiThreadForm3;
        static ProgressReporter timer1 = null;
        static ProgressReporter timer2 = null;
        static ProgressReporter timer3 = null;
        
        private static void InitializeTimers()
        {
            if (timer1 == null)
            {
                timer1 = new ProgressReporter();
                timer2 = new ProgressReporter();
                timer3 = new ProgressReporter();
            }
        }

        public static void GetNajczestszeLiczby(int ilosc, bool showForm = true)
        {
            if (ilosc < 1 || ilosc > 3)
                throw new ArgumentException("Wspomagane jest liczenie występowania kombinacji od 1 do 3 liczb");

            PrzeliczLiczby(ilosc, showForm);
        }

        private static void PrzeliczLiczby(int ilosc, bool showForm)
        {
            int iloscLosowan = Tools.Lotto.LottoLosowaniaTools.IloscLosowan;
            FormBackgroundWorkMultiThread f =null;
            ProgressReporter timer = null;
            InitializeTimers();
            if (ilosc == 1)
            {
                multiThreadForm1 = new FormBackgroundWorkMultiThread("Szacowanie ...");
                timer = timer1;
                f = multiThreadForm1;
            }
            if (ilosc == 2)
            {
                multiThreadForm2 = new FormBackgroundWorkMultiThread("Szacowanie ...");
                timer = timer2;
                f = multiThreadForm2;
            }
            if (ilosc == 3)
            {
                multiThreadForm3 = new FormBackgroundWorkMultiThread("Szacowanie ...");
                timer = timer3;
                f = multiThreadForm3;
            }
            if (iloscLosowan < 1)
            {
                f.ShowDxInfoDialog("Brak losowań");
                return;
            }
            int max =0;
            if (ilosc == 1)
                max = 49;
            else if (ilosc == 2)
                max = LottoLosowaniaTools.LICZBA_KOMBINACJI_2_Z_49;
            else if (ilosc == 3)
                max = LottoLosowaniaTools.LICZBA_KOMBINACJI_3_Z_49;

            f.BackgroundControl.MaxProgress = (max);
            if (!showForm)
            {
                f.StartPosition = FormStartPosition.Manual;
                f.Location = new System.Drawing.Point(-1000, -1000);
                f.ShowInTaskbar = false;
            }
            Dictionary<int,int> dic = new Dictionary<int, int>();
            dic.Add(1, 3);
            dic.Add(3, 6);
            dic.Add(6, 10);
            dic.Add(10, 15);
            dic.Add(15, 20);
            dic.Add(20, 27);
            dic.Add(27, 34);
            dic.Add(34, 51 - ilosc);

            foreach (KeyValuePair<int,int> kvp in dic)
            {
                if (ilosc == 1)
                    f.AddAction(new Action<int, int>(Szacuj1), new object[] { kvp.Key, kvp.Value });
                else if (ilosc == 2)
                    f.AddAction(new Action<int, int>(Szacuj2), new object[] { kvp.Key, kvp.Value });
                else if (ilosc == 3)
                    f.AddAction(new Action<int, int>(Szacuj3), new object[] { kvp.Key, kvp.Value });
            }
            timer.Form = f;
            timer.Start();
            DialogResult dlg = f.ShowDialog();
            timer.Stop();
            switch (dlg)
            {
                case System.Windows.Forms.DialogResult.Cancel:
                    f.ShowDxInfoDialog("Przerwa na życzenie użytkownika");
                    break;
                case System.Windows.Forms.DialogResult.Abort:
                    f.ShowDxErrorDialog("Błąd wyliczania.");
                    break;
                case System.Windows.Forms.DialogResult.OK:
                    if (ilosc == 1)
                    {
                        _NajczestszeLiczbyList.Sort(new NajczestszeLiczbyComparer());
                        _IloscNajczestszeLiczby = _NajczestszeLiczbyList.Count;
                        _NajczestszeLiczbyPrecashed = true;
                    }
                    if (ilosc == 2)
                    {
                        _NajczestszeDwojkiList.Sort(new NajczestszeLiczbyComparer());
                        _IloscNajczestszeDwojki = _NajczestszeDwojkiList.Count;
                        _NajczestszeDwojkiPrecashed = true;
                    }
                    if (ilosc == 3)
                    {
                        _NajczestszeTrojkiList.Sort(new NajczestszeLiczbyComparer());
                        _IloscNajczestszeTrojki = _NajczestszeTrojkiList.Count;
                        _NajczestszeTrojkiPrecashed = true;
                    }
                    break;
            }
        }
        private static void Szacuj3(int minNum, int maxNum)
        {
            List<object[]> newItems = new List<object[]>();
            int count = 0;
            List<List<int>> liczby1, liczby2,liczby3;
            List<NajczestszeLiczby> list = new List<NajczestszeLiczby>();

            for (int l1=minNum; l1 < maxNum; l1++)
            {
                liczby1 = Getkombinacje(LottoLosowaniaTools.LosowaniaList, l1);
                for (int l2 = l1 + 1; l2 < 49; l2++)
                {
                    liczby2 = Getkombinacje(liczby1, l2);
                    for (int l3 = l2 + 1; l3 < 50; l3++)
                    {
                        if (multiThreadForm3.BackgroundControl.IsCanceling)
                            return;
                        liczby3 = Getkombinacje(liczby2, l3);
                        count = liczby3.Count;
                        list.Add(new NajczestszeLiczby(count, new List<int>() { l1, l2, l3 }));
                        timer3.Progress++;
                    }
                }
            }
            lock (lockNajczestszeTrojki)
            {
                _NajczestszeTrojkiList.AddRange(list);
            }
        }
        private static void Szacuj2(int minNum, int maxNum)
        {
            List<object[]> newItems = new List<object[]>();
            List<List<int>> liczby1, liczby2;
            List<NajczestszeLiczby> list = new List<NajczestszeLiczby>();

            int count = 0;
            for (int l1=minNum; l1 < maxNum; l1++)
            {
                liczby1 = Getkombinacje(LottoLosowaniaTools.LosowaniaList, l1);
                for (int l2 = l1 + 1; l2 < 50; l2++)
                {
                    if (multiThreadForm2.BackgroundControl.IsCanceling)
                        return;
                    liczby2 = Getkombinacje(liczby1, l2);
                    count = liczby2.Count;

                    list.Add(new NajczestszeLiczby(count, new List<int>() { l1, l2 }));

                    timer2.Progress++;
                }
            }
            lock (lockNajczestszeDwojki)
            {
                _NajczestszeDwojkiList.AddRange(list);
            }
        }
        private static void Szacuj1(int minNum, int maxNum)
        {
            List<object[]> newItems = new List<object[]>();
            List<int> kombinacja = new List<int>();
            int count = 0;
            List<List<int>> liczby1;
            List<NajczestszeLiczby> list = new List<NajczestszeLiczby>();

            for (int l1=minNum; l1 < maxNum; l1++)
            {
                if (multiThreadForm1.BackgroundControl.IsCanceling)
                    return;
                liczby1 = Getkombinacje(LottoLosowaniaTools.LosowaniaList, l1);
                count = liczby1.Count;

                list.Add(new NajczestszeLiczby(count, new List<int>() { l1 }));
                timer3.Progress++;
            }
            lock (lockNajczestszeLiczby)
            {
                _NajczestszeLiczbyList.AddRange(list);
            }
        }
        private static List<List<int>> Getkombinacje(List<List<int>> kombinacje, int liczba)
        {
            List<List<int>> list = new List<List<int>>();
            int max = kombinacje.Count;
            for (int i=0; i < max; i++)
            {
                if (kombinacje[i].Contains(liczba))
                    list.Add(kombinacje[i]);
            }
            return list;
        }

    }

    public class NajczestszeLiczby
    {
        public int Count
        {
            get;
            private set;
        }
        public List<int> Liczby
        {
            get;
            private set;
        }
        public string LiczbyStr
        {
            get;
            private set;
        }
        public decimal Procentowo
        {
            get;
            private set;
        }
        public NajczestszeLiczby(int count, List<int> liczby)
        {
            Count = count;
            Liczby = liczby;
            LiczbyStr = "";
            for (int i=0; i < liczby.Count; i++)
            {
                LiczbyStr += ", " + liczby[i].ToString();
            }
            LiczbyStr = LiczbyStr.TrimStart(' ', ',');
            Procentowo = (decimal)count / (decimal)LottoLosowaniaTools.IloscLosowan;

        }
        public NajczestszeLiczby Clone()
        {
            return new NajczestszeLiczby(Count, Liczby);
        }
    }

    public class NajczestszeLiczbyComparer : IComparer<NajczestszeLiczby>
    {

        #region IComparer<NajczestszeLiczby> Members

        public int Compare(NajczestszeLiczby x, NajczestszeLiczby y)
        {
            return y.Count.CompareTo(x.Count);
        }

        #endregion
    }
}

